package io.github.stary1993.kite.admin.controller;

import cn.hutool.core.date.LocalDateTimeUtil;
import io.github.stary1993.kite.oss.support.OssTemplate;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.media.ExampleObject;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.Cleanup;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import software.amazon.awssdk.services.s3.model.Bucket;
import software.amazon.awssdk.services.s3.model.BucketCannedACL;
import software.amazon.awssdk.services.s3.model.HeadObjectResponse;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * oss 对外提供服务
 *
 * @author stary1993
 * <p>
 */
@Validated
@RestController
@RequiredArgsConstructor
@RequestMapping("/oss")
@Tag(name = "OSS接口模块")
public class OssController {

    /**
     * OSS操作模板
     */
    private final OssTemplate ossTemplate;

    @Operation(summary = "创建Bucket")
    @Parameters({
            @Parameter(name = "bucketName", description = "Bucket名称", required = true),
            @Parameter(name = "acl", description = "Bucket ACL", example = "private",
                    examples = {
                            @ExampleObject(value = "private"),
                            @ExampleObject(value = "public-read"),
                            @ExampleObject(value = "public-read-write"),
                            @ExampleObject(value = "authenticated-read")
                    })
    })
    @PostMapping("/bucket/{bucketName}")
    public Map<String, Object> createBucket(@PathVariable @NotBlank String bucketName, @RequestParam(required = false) String acl) {
        ossTemplate.createBucket(bucketName, BucketCannedACL.fromValue(acl));
        Bucket bucket = ossTemplate.getBucket(bucketName);
        Map<String, Object> responseBody = new HashMap<>();
        if (bucket != null) {
            responseBody.put("name", bucket.name());
            responseBody.put("creationDate", LocalDateTimeUtil.of(bucket.creationDate()));
            responseBody.put("bucketArn", bucket.bucketArn());
            responseBody.put("bucketRegion", bucket.bucketRegion());
        }
        return responseBody;
    }
    @PreAuthorize("hasAuthority('bucket:list')")
    @Operation(summary = "获取Bucket列表")
    @GetMapping("/bucket/list")
    public List<Map<String, Object>> getBuckets() {
        return ossTemplate.getAllBuckets().stream().map(bucket -> {
            Map<String, Object> responseBody = new HashMap<>();
            responseBody.put("name", bucket.name());
            responseBody.put("creationDate", LocalDateTimeUtil.of(bucket.creationDate()));
            responseBody.put("bucketArn", bucket.bucketArn());
            responseBody.put("bucketRegion", bucket.bucketRegion());
            return responseBody;
        }).collect(Collectors.toList());
    }

    @Operation(summary = "获取Bucket信息")
    @Parameter(name = "bucketName", description = "Bucket名称", required = true)
    @GetMapping("/bucket/{bucketName}")
    public Map<String, Object> getBucket(@PathVariable @NotBlank String bucketName) {
        Bucket bucket = ossTemplate.getBucket(bucketName);
        Map<String, Object> responseBody = new HashMap<>();
        if (bucket != null) {
            responseBody.put("name", bucket.name());
            responseBody.put("creationDate", LocalDateTimeUtil.of(bucket.creationDate()));
            responseBody.put("bucketArn", bucket.bucketArn());
            responseBody.put("bucketRegion", bucket.bucketRegion());
        }
        return responseBody;
    }

    @Operation(summary = "删除Bucket")
    @Parameter(name = "bucketName", description = "Bucket名称", required = true)
    @DeleteMapping("/bucket/{bucketName}")
    @ResponseStatus(HttpStatus.ACCEPTED)
    public void deleteBucket(@PathVariable @NotBlank String bucketName) {
        ossTemplate.deleteBucket(bucketName);
    }

    @Operation(summary = "上传对象")
    @Parameter(name = "bucketName", description = "Bucket名称", required = true)
    @SneakyThrows
    @PostMapping("/object/{bucketName}")
    public Map<String, Object> createObject(@RequestParam("file") @NotNull MultipartFile object,
                                            @PathVariable @NotBlank String bucketName) {
        @Cleanup
        InputStream inputStream = object.getInputStream();
        String name = object.getOriginalFilename();
        ossTemplate.putObject(bucketName, name, inputStream, object.getSize(), object.getContentType());
        HeadObjectResponse objectInfo = ossTemplate.getObjectInfo(bucketName, name);

        Map<String, Object> responseBody = new HashMap<>();
        responseBody.put("key", name);
        responseBody.put("bucketName", bucketName);
        responseBody.put("eTag", objectInfo.eTag());
        responseBody.put("lastModified", LocalDateTimeUtil.of(objectInfo.lastModified()));
        responseBody.put("size", objectInfo.contentLength());
        return responseBody;
    }


    @Operation(summary = "根据前缀过滤对象")
    @Parameters({
            @Parameter(name = "bucketName", description = "Bucket名称", required = true),
            @Parameter(name = "prefix", description = "对象前缀", required = true)
    })
    @GetMapping("/object/{bucketName}/{prefix}")
    public List<Map<String, Object>> filterObject(@PathVariable @NotBlank String bucketName,
                                                  @PathVariable @NotBlank String prefix) {
        return ossTemplate.getAllObjectsByPrefix(bucketName, prefix).stream().map(object -> {
            Map<String, Object> responseBody = new HashMap<>();
            responseBody.put("key", object.key());
            responseBody.put("bucketName", bucketName);
            responseBody.put("eTag", object.eTag());
            responseBody.put("lastModified", LocalDateTimeUtil.of(object.lastModified()));
            responseBody.put("size", object.size());
            return responseBody;
        }).collect(Collectors.toList());
    }


    @Operation(summary = "获取对象URL")
    @Parameters({
            @Parameter(name = "bucketName", description = "Bucket名称", required = true),
            @Parameter(name = "objectName", description = "对象名称", required = true),
            @Parameter(name = "expires", description = "过期时间(分钟)", required = true)
    })
    @GetMapping("/object/{bucketName}/{objectName}/{expires}")
    public Map<String, Object> getObjectUrl(@PathVariable @NotBlank String bucketName,
                                            @PathVariable @NotBlank String objectName, @PathVariable @NotNull Integer expires) {
        Map<String, Object> responseBody = new HashMap<>(8);
        responseBody.put("bucket", bucketName);
        responseBody.put("key", objectName);
        responseBody.put("url", ossTemplate.getObjectURL(bucketName, objectName, expires));
        responseBody.put("expires", expires);
        return responseBody;
    }

    @Operation(summary = "获取对象上传URL")
    @Parameters({
            @Parameter(name = "bucketName", description = "Bucket名称", required = true),
            @Parameter(name = "objectName", description = "对象名称", required = true),
            @Parameter(name = "expires", description = "过期时间(分钟)", required = true)
    })
    @GetMapping("/object/put/{bucketName}/{objectName}/{expires}")
    public Map<String, Object> getPutObjectUrl(@PathVariable @NotBlank String bucketName,
                                               @PathVariable @NotBlank String objectName, @PathVariable @NotNull Integer expires) {
        Map<String, Object> responseBody = new HashMap<>(8);
        responseBody.put("bucket", bucketName);
        responseBody.put("key", objectName);
        responseBody.put("url", ossTemplate.getPutObjectURL(bucketName, objectName, expires));
        responseBody.put("expires", expires);
        return responseBody;
    }


    @Operation(summary = "获取对象公共URL")
    @Parameters({
            @Parameter(name = "bucketName", description = "Bucket名称", required = true),
            @Parameter(name = "objectName", description = "对象名称", required = true)
    })
    @GetMapping("/object/public/{bucketName}/{objectName}")
    public Map<String, Object> getPublicObjectUrl(@PathVariable @NotBlank String bucketName,
                                                  @PathVariable @NotBlank String objectName) {
        Map<String, Object> responseBody = new HashMap<>(8);
        responseBody.put("bucket", bucketName);
        responseBody.put("key", objectName);
        responseBody.put("url", ossTemplate.getObjectURL(bucketName, objectName));
        return responseBody;
    }

    @Operation(summary = "删除对象")
    @Parameters({
            @Parameter(name = "bucketName", description = "Bucket名称", required = true),
            @Parameter(name = "objectName", description = "对象名称", required = true)
    })
    @ResponseStatus(HttpStatus.ACCEPTED)
    @DeleteMapping("/object/{bucketName}/{objectName}")
    public void deleteObject(@PathVariable @NotBlank String bucketName, @PathVariable @NotBlank String objectName) {
        ossTemplate.deleteObject(bucketName, objectName);
    }

}